package com.study.demo.disruptor.service;

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.study.demo.disruptor.entity.ConsumerWorkHandler;
import com.study.demo.disruptor.entity.LongEvent;
import com.study.demo.disruptor.entity.LongEventFactory;
import com.study.demo.disruptor.entity.LongEventProducer;

import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Disruptor的一些核心介绍
 * 1.RingBuffer
 * RingBuffer是其核心，生产者向RingBuffer中写入元素，消费者从RingBuffer中消费元素。
 * 随着你不停地填充这个buffer（可能也会有相应的读取），这个序号会一直增长，直到绕过这个环。
 * 槽的个数是2的N次方更有利于基于二进制的计算机进行计算。（注：2的N次方换成二进制就是1000，100，10，1这样的数字， sequence & （array length－1） = array index，比如一共有8槽，3&（8－1）=3，HashMap就是用这个方式来定位数组元素的，这种方式比取模的速度更快。）
 * 会预先分配内存,可以做到完全的内存复用。在系统的运行过程中，不会有新的空间需要分配或者老的空间需要回收。因此，可以大大减少系统分配空间以及回收空间的额外开销。
 *
 * 2.消费者等待策略
 * BlockingWaitStrategy：这是默认的策略。使用锁和条件进行数据的监控和线程的唤醒。因为涉及到线程的切换，是最节省CPU，但在高并发下性能表现最糟糕的一种等待策略。
 * SleepingWaitStrategy:会自旋等待数据，如果不成功，才让出cpu，最终进行线程休眠，以确保不占用太多的CPU数据，因此可能产生比较高的平均延时。比较适合对延时要求不高的场合，好处是对生产者线程的影响最小。典型的应用场景是异步日志。
 * YieldingWaitStrategy:用于低延时的场合。消费者线程不断循环监控缓冲区变化，在循环内部，会使用Thread.yield()让出cpu给别的线程执行时间。
 * BusySpinWaitStrategy:开启的是一个死循环监控，消费者线程会尽最大努力监控缓冲区变化，因此，CPU负担比较大
 *
 * 3.Disruptor的应用场景
 * Disruptor号称能够在一个线程里每秒处理 6百万订单
 * Disruptor实际上内部是使用环形队列来实现的，所以一般来说，在消费者和生产者的场景中都可以考虑使用Disruptor。
 * 比如像日志处理之类的。
 *
 * 像是java阻塞队列的替代
 *
 * RingBuffer，它是一个环（收尾相接的环），用作在不同上下文（线程）间传递数据的buffer.
 * RingBuffer拥有一个序号，这个序号指向数组中下一个可用元素
 * RingBuffer：基于数组的缓存实现，也是创建sequencer与定义WaitStrategy的入口
 */

public class LongEventMain {
    public static void main(String[] args) throws Exception {

        //创建线程池
        Executor executor = Executors.newCachedThreadPool();

        //事件工厂
        LongEventFactory factory = new LongEventFactory();

        // ringBuffer 的缓冲区的大小是1024
        int bufferSize = 1024;

        // 创建一个disruptor
        //ProducerType.MULTI:创建一个环形缓冲区支持多事件发布到一个环形缓冲区
        //在构建Disruptor实例的时候,需要指定生产者是单生产者（ProducerType.SINGLE）还是多生产者（ProducerType.MULTI）
        // 消费者的等待策略
        Disruptor<LongEvent> disruptor = new Disruptor<>(factory, bufferSize, executor, ProducerType.MULTI, new BlockingWaitStrategy());

        // 创建一个消费者
//        disruptor.handleEventsWith(new ConsumerEventHandler());

        //多个消费者，每个消费者竞争消费不同数据
         disruptor.handleEventsWithWorkerPool(new ConsumerWorkHandler(1),new ConsumerWorkHandler(2));

        // 启动并初始化disruptor
        disruptor.start();

        // 获取已经初始化好的ringBuffer
        RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();

        // 获取已经初始化好的ringBuffer
        LongEventProducer producer = new LongEventProducer(ringBuffer);

        //定义8字节的buffer
        ByteBuffer bb = ByteBuffer.allocate(8);

        System.out.println("执行开始......");

        for (int i = 0;i < 10; i++) {
            //存入数据
            bb.putInt(0, i);
            //加入到ringBuffer
            producer.publishData(bb);
            TimeUnit.SECONDS.sleep(1);
        }

        System.out.println("执行结束......");
    }
}
